package com.secure.support.feign;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.secure.annotation.ApiSecure;
import com.secure.bean.ClientAppKey;
import com.secure.constant.RequestConstant;
import com.secure.enums.ResponseDecryptEnum;
import com.secure.handler.EncryptDecryptHandler;
import com.secure.properties.SecureClientProperties;
import feign.FeignException;
import feign.RequestTemplate;
import feign.Response;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.HttpMessageConverterCustomizer;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

public class FeignResponseDecryptDecoder extends SpringDecoder {

    private final SecureClientProperties secureClientProperties;
    private final EncryptDecryptHandler encryptDecryptHandler;

    public FeignResponseDecryptDecoder(ObjectFactory<HttpMessageConverters> messageConverters, ObjectProvider<HttpMessageConverterCustomizer> customizers, SecureClientProperties secureClientProperties, EncryptDecryptHandler encryptDecryptHandler) {
        super(messageConverters, customizers);
        this.secureClientProperties = secureClientProperties;
        this.encryptDecryptHandler = encryptDecryptHandler;
    }

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        ClientAppKey clientAppKey = getClientAppKey(response);
        if (isNeedDecryptResponse(response, clientAppKey)) {
            String encryptBody = StreamUtils.copyToString(response.body().asInputStream(), StandardCharsets.UTF_8);
            String plainBody = encryptDecryptHandler.decryptByPublicKey(encryptBody, clientAppKey.getEncryptDecrypt().getPublicKey());
            return super.decode(response.toBuilder().body(plainBody, StandardCharsets.UTF_8).build(), type);
        } else {
            //响应如果是密文，并且响应不是字符串类型，需要把响应类型改成字符串类型
            String body = StreamUtils.copyToString(response.body().asInputStream(), StandardCharsets.UTF_8);
            if (response.headers().containsKey(RequestConstant.ENCRYPT_RESPONSE) || !JSONUtil.isTypeJSON(body)) {
                type = String.class;
            }
            return super.decode(response.toBuilder().body(body, StandardCharsets.UTF_8).build(), type);
        }
    }

    /**
     * 判断是否需要解密响应
     *
     * @param response 响应
     * @return
     */
    private boolean isNeedDecryptResponse(Response response, ClientAppKey clientAppKey) {
        RequestTemplate requestTemplate = response.request().requestTemplate();
        Method method = requestTemplate.methodMetadata().method();
        if (method.isAnnotationPresent(ApiSecure.class) && ObjectUtil.isNotNull(clientAppKey)) {
            ApiSecure annotation = method.getAnnotation(ApiSecure.class);
            return ResponseDecryptEnum.UNSPECIFY == annotation.decryptResponse() ? clientAppKey.isDecryptResponse() : ResponseDecryptEnum.DECRYPT == annotation.decryptResponse();
        }
        return false;
    }

    /**
     * 获取某个服务安全配置
     *
     * @param response 响应
     * @return
     */
    private ClientAppKey getClientAppKey(Response response) {
        RequestTemplate requestTemplate = response.request().requestTemplate();
        String serviceName = requestTemplate.feignTarget().name();
        if (StrUtil.isNotBlank(serviceName)) {
            return secureClientProperties.getAppKeyConfig().get(serviceName);
        }
        return null;
    }
}
